package alibaba;

import java.io.File;
import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.sun.net.httpserver.HttpServer;

public class AlibabaServer {

	private static char fileSeprator = File.separatorChar;
	private static String rootDir;
	private static Listing listing;

	public static void main(String[] args) {

		doCommand(args);
		String listingFileName = rootDir + fileSeprator + "listing.dat";
		File listingFile = new File(listingFileName);
		if (!listingFile.exists()) {
			listing = new Listing();
			listing.setListOfFiles(new HashMap<String,ListingEntry>());
			Utils.storeListing(listing, listingFile);
		}
		listing = Utils.loadListing(listingFile);
		deleteUnreferencedFiles();
		deleteBadEntries();
		listen(8080);
	}

	private static void deleteUnreferencedFiles() {

		Set<String> localFileNames = new HashSet<String>();
		for (ListingEntry entry : listing.getListOfFiles().values()) {
			localFileNames.add(entry.getLocalName());
		}
		File rootDirectory = new File(rootDir);
		for (File file : rootDirectory.listFiles()) {
			if (!localFileNames.contains(file.getAbsolutePath()) &&
					! file.getName().equals("listing.dat")) {
				file.delete();
			}
		}
	}

	private static void deleteBadEntries() {

		File rootDirectory = new File(rootDir);
		for (File file : rootDirectory.listFiles()) {
			if (!file.exists())
				listing.getListOfFiles().remove(file.getName());
		}
	}

	private static void listen(int i) {
		InetSocketAddress addr = new InetSocketAddress(8080);
		HttpServer server = null;
		try {
			server = HttpServer.create(addr, 0);
		} catch (BindException e) {
			System.out.println("Cannot create server, address already in use");
			e.printStackTrace();
			return;
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}

		initialize();
		
		server.createContext("/", new AlibabaServerHandler());
		server.start();
		System.out.println("Server is listening on port 8080" );
	}

	private static void initialize() {
		AlibabaServerHandler.setListing(listing);
		
		int fileEnumerator = -1;
		Map<String,Map<String, DirectoryInfo>> memoryStoredDirMap = DirectoryInfo.getMemoryStoredDirMaps();
		Map<String,String> sigs = new HashMap<String, String>(); 
		sigs.put(Utils.zeroSig(), "");//to avoid insertion of empty sig
		Map<String,Set<Integer> > sigToSnapshots = new HashMap<String, Set<Integer>>();
		
		// initialize directories map and signatures
		for (ListingEntry entry : listing.getListOfFiles().values()) {
			int fileNameInt = Utils.getFileNameInt(entry.getLocalName());
			if (fileNameInt > fileEnumerator)
				fileEnumerator = fileNameInt;
			String user = entry.getOwner();
			if (!memoryStoredDirMap.containsKey(user)){
				memoryStoredDirMap.put(user, new HashMap<String, DirectoryInfo>());
			}
			DirectoryInfo dirInfo = null;
			String dirName = Utils.extractVirtualDirName(entry.getFilename());
			Map<String, DirectoryInfo> directories = memoryStoredDirMap.get(user);
			dirInfo = directories.get(dirName);
			if (dirInfo == null)
				dirInfo = new DirectoryInfo(dirName,user);
			dirInfo.addFile(entry);
			
			//put the sigs in dictionary 
			byte[] fileHash = entry.getFileHash();
			String sig = Utils.zeroSig();
			try {
				sig = Base64.encodeBytes(fileHash, Base64.URL_SAFE);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (!sigs.containsKey(sig)){
				sigs.put(sig, entry.getLocalName());
			}
			if (!sigToSnapshots.containsKey(sig) && entry.getSnapshotVersion() != null) {
				
				Set<Integer> snapshots = new TreeSet<Integer>();
					for (int snapshot : entry.getSnapshotVersion())
						snapshots.add(snapshot);
				sigToSnapshots.put(sig, snapshots);
			}
		}
		fileEnumerator++;
		AlibabaServerHandler.setFileEnumerator(fileEnumerator);
		AlibabaServerHandler.setDirMaps(memoryStoredDirMap);
		AlibabaServerHandler.setSignatures(sigs);
		AlibabaServerHandler.setSigToSnapshots(sigToSnapshots);
		
	}

	private static void doCommand(String[] args) {

		if (args.length != 1) {
			System.out.println("Usage: AlibabaServer <server_root_directory>");
			System.exit(-1);
		}

		String commandString = args[0];
		File inputRootDir = new File(commandString);
		if (!inputRootDir.exists() || !inputRootDir.isDirectory()) {
			System.out.println("Usage: AlibabaServer <server_root_directory>");
			System.out.println(commandString + " is not a valid directory in server");
			System.out.println(inputRootDir.isDirectory());
			System.exit(-1);
		}
		setRootDir(inputRootDir.getAbsolutePath());
		AlibabaServerHandler.setRootDir(inputRootDir.getAbsolutePath());
	}

	private static void setRootDir(String absolutePath) {
		rootDir = absolutePath;		
	}

	protected static  void printMainHelp() {
		System.out.println("Usage: AlibabaServer <server_root_directory>");
		System.exit(-1);
	}
}

